/***************************************************************************
 *cr
 *cr            (C) Copyright 1995-2019 The Board of Trustees of the
 *cr                        University of Illinois
 *cr                         All Rights Reserved
 *cr
 ***************************************************************************/

/***************************************************************************
 * RCS INFORMATION:
 *
 *      $RCSfile: FileChooserFltkMenu.C,v $
 *      $Author: johns $        $Locker:  $             $State: Exp $
 *      $Revision: 1.56 $       $Date: 2019/01/17 21:20:59 $
 *
 ***************************************************************************
 * DESCRIPTION:
 *  generated by Fast Light User Interface Designer (fluid) version 1.0100
 ***************************************************************************/

#include <assert.h>
#include <stdio.h>
#include <FL/forms.H>
#include <FL/Fl_Menu_.H>
#include <FL/Fl_Round_Button.H>
#include <FL/Fl_Multi_Browser.H>
#include <FL/Fl_Int_Input.H>
#include "FileChooserFltkMenu.h"
#include "VMDApp.h"
#include "Command.h"
#include "molfile_plugin.h"
#include "utilities.h"
#include "Inform.h"

void FileChooserFltkMenu::browse_cb(Fl_Widget *w, void *v) {
  FileChooserFltkMenu *self = (FileChooserFltkMenu *)v;
  self->browse_files();
}


void FileChooserFltkMenu::browse_files() {
  char mask[4096];
  strcpy(mask, "*");

  const char *filetype = "All files";
  int val = filetypechooser->value();
  if (val) {
    const Fl_Menu_Item *menulist = filetypechooser->menu();
    const char *extension = ((molfile_plugin_t *)menulist[val].user_data())->filename_extension;

    if (extension) {
      int cnt = 0; // number of extensions added

      // add all extensions to the filename selection mask
      char *extbuf = strdup(extension);
      int extlen = strlen(extbuf);
      char *extcur = extbuf;
      char *extnext = NULL;
      while ((extcur - extbuf) < extlen) {
        extnext = strchr(extcur, ','); // find next extension string
        if (extnext) {
          *extnext = '\0'; // NUL terminate this extension string
          extnext++;       // step to beginning of next extension string
        } else {
          extnext = extbuf + extlen; // no more extensions, last time through
        }

        if (cnt == 0) {
          sprintf(mask, "*.%s", extcur); // overwrite if the first extension
        } 
// XXX current file browser code doesn't do well with multiple 
// filename extensions, so we just drop subsequent ones 
#if 0
        else {
          char tmpmask[256];
          sprintf(tmpmask, ",*.%s", extcur);
          strcat(mask, tmpmask);         // concat subsequent extensions
        } 
#endif
        cnt++;

        extcur = extnext;
      }
      free(extbuf);

      filetype = ((molfile_plugin_t *)menulist[val].user_data())->name;
    }
  }
  char *result = app->vmd_choose_file(
      "Choose a molecule file", mask, filetype, 0);
  if (result) {
    add_file(result);
    delete [] result;
  }
}


void FileChooserFltkMenu::filenameinput_cb(Fl_Widget *w, void *v) {
  FileChooserFltkMenu *menu = (FileChooserFltkMenu *)v;
  menu->add_file(((Fl_Input *)w)->value());
}

  
void FileChooserFltkMenu::add_file(const char *s) {
  if (!s) return;
  filenameinput->value(s);
  filenameinput->position(filenameinput->size(),0);

  // Figure out what kind of file this is.  If the file type chooser has
  // an item selected, don't change it.  If it's "unknown", try to guess the 
  // file type based on the filename extension.
  if (filetypechooser->value() > 0) {
    update_checkboxes();
    return; // already have a file type.
  }
  filetypechooser->value(0);
  const char *filetype = app->guess_filetype(s);
  if (filetype) {
    const Fl_Menu_Item *menu = filetypechooser->menu();
    if (menu != NULL) {
      // skip over the special "Automatically" item
      for (int j=1; j<menu->size()-2; j++) {
        const vmdplugin_t *plugin = (const vmdplugin_t *) menu[j].user_data();
        if (plugin != NULL && !strcmp(plugin->name, filetype)) {
          filetypechooser->value(j);
          break;
        }
      }
    }
  }
  update_checkboxes();
}


void FileChooserFltkMenu::update_molchooser(int selmol) {
  fill_fltk_molchooser(molchooser, app, "New Molecule");
  if (selmol >= -1) molchooser->value(selmol+1);
}


// compare plugin names, for sorted display in the GUI
static int compare_plugin_name(const void *p1, const void *p2) {
  return strcmp( (*(vmdplugin_t **)p1)->prettyname, (*(vmdplugin_t **)p2)->prettyname);
}


// Create the file type chooser from all the plugins.  Set the chooser to
// show "automatically", the default.  Store the plugin in the user data of 
// the menu item.
void FileChooserFltkMenu::update_filetypes() {
  PluginList plugins;
  int n;

  // prepare the file type chooser and add the special "Automatically" choice 
  filetypechooser->clear();
  filetypechooser->add("Automatically");

  // 
  // retrieve list of mol file reader plugins
  // 
  n = app->list_plugins(plugins, "mol file reader");

  // sort plugins by their "pretty" name
  qsort(&(plugins[0]), n, sizeof(vmdplugin_t *), compare_plugin_name);

  // add the plugins to the chooser, storing the plugin pointer in the menu 
  for (int j=0; j<n; j++) {
    vmdplugin_t *p = plugins[j];
    filetypechooser->add(p->prettyname, 0, NULL, p); // XXX valgrind FMR
  }


  // 
  // retrieve list of mol file converter plugins
  // 
  plugins.clear();
  n = app->list_plugins(plugins, "mol file converter");

  // sort converter plugins by their "pretty" name
  qsort(&(plugins[0]), n, sizeof(vmdplugin_t *), compare_plugin_name);

  // add the plugins to the chooser, storing the plugin pointer in the menu 
  for (int k=0; k<n; k++) {
    vmdplugin_t *p = plugins[k];
    char *buf = new char[strlen(p->prettyname)+16];
    sprintf(buf, "Convert from:/%s", p->name);
    filetypechooser->add(buf, 0, NULL, p); // XXX valgrind FMR
    delete [] buf;
  }

  // set the default value to "Automatically"
  filetypechooser->value(0);
}


void FileChooserFltkMenu::filetype_cb(Fl_Widget *w, void *v) {
  FileChooserFltkMenu *self = (FileChooserFltkMenu *)v;
  self->update_checkboxes();
}

  
// reset the activity and value of checkboxes based on the current
// value of filetypechooser
void FileChooserFltkMenu::update_checkboxes() {
  has_structure = has_bonds = has_timesteps = has_graphics = 
    has_volumetric = 0;
  timestepgroup->deactivate();
  datasetbrowser->deactivate();
  if (!filetypechooser->value()) return;

  molfile_plugin_t *plugin = 
    (molfile_plugin_t *)filetypechooser->mvalue()->user_data();
  has_structure = (plugin->read_structure != NULL);
  has_bonds = (plugin->read_bonds != NULL);
  has_timesteps = (plugin->read_next_timestep != NULL);
  has_graphics = (plugin->read_rawgraphics != NULL);
  has_volumetric = (plugin->read_volumetric_metadata != NULL);

  if (has_timesteps) {
    timestepgroup->activate();
    firstinput->value("0");
    lastinput->value("-1");
    strideinput->value("1");
    loadinbackgroundbutton->setonly();
  }

  if (has_volumetric) { 
    datasetbrowser->activate(); 
    datasetbrowser->clear(); 
    const char *filename = filenameinput->value();
    if (filename == NULL || !strlen(filename)) {
      msgInfo << "Can't show volumetric data; no filename selected yet" << sendmsg;
      return; 
    }
    int natoms = -1;
    void *rv = plugin->open_file_read(filename, plugin->name, &natoms);
    if (!rv) {
      msgErr << "Unable to open file with plugin of type " <<  plugin->name << sendmsg;
      return;
    }
    molfile_volumetric_t *tmp;
    int nsets = -1;
    if (plugin->read_volumetric_metadata(rv, &nsets, &tmp)) {
      msgErr << "read_volumetric_metadata returned error" << sendmsg;
      plugin->close_file_read(rv);
      return;
    }
    if (nsets <= 0) {
      msgInfo << "No volumetric datasets found" << sendmsg;
      return;
    }
    for (int n=0; n<nsets; n++) {
      molfile_volumetric_t *v = tmp+n;
      char *buf = new char[strlen(v->dataname)+5];
      sprintf(buf, "%d: %s", n+1, v->dataname);
      datasetbrowser->add(buf);
      delete [] buf;
    }
    datasetbrowser->select(1, datasetbrowser->size());
    plugin->close_file_read(rv);
  }
}


void FileChooserFltkMenu::load_cb(Fl_Widget *, void *v) {
  FileChooserFltkMenu *self = (FileChooserFltkMenu *)v;
  self->load_file(self->filenameinput->value());
}


void FileChooserFltkMenu::load_file(const char *filename) {
  const Fl_Menu_Item *menu = filetypechooser->menu();

  // Make sure a file is selected
  if (!filename || !strlen(filename)) {
    fl_alert("Please select a file.");
    return;
  }

  // Make sure a plugin has been assigned 
  if (filetypechooser->value() == 0) {
    const char *filetype = app->guess_filetype(filename);
    if (filetype) {
      if (menu != NULL) {
        // skip over the special "Automatically" item
        for (int j=1; j<menu->size()-2; j++) {
          const vmdplugin_t *plugin = (const vmdplugin_t *) menu[j].user_data();
          if (plugin != NULL && !strcmp(plugin->name, filetype)) {
            filetypechooser->value(j);
            update_checkboxes();
            break;
          }
        }
      }
    } else {
      fl_alert("Please select a file type.");
      return;
    }
  }

  const vmdplugin_t *plugin = (const vmdplugin_t *) menu[filetypechooser->value()].user_data();
  const char *filetype = plugin->name;

  int molid = app->molecule_id(molchooser->value()-1);

  FileSpec spec;
  sscanf(firstinput->value(), "%d", &spec.first);
  sscanf(lastinput->value(), "%d", &spec.last);
  sscanf(strideinput->value(), "%d", &spec.stride);
  if (spec.first < 0) spec.first = 0;
  if (spec.last >= 0 && spec.last < spec.first) spec.last = spec.first;
  if (spec.stride < 1) spec.stride = 1;
  spec.waitfor = allatoncebutton->value() ? 
                 FileSpec::WAIT_ALL : FileSpec::WAIT_BACK;

  ResizeArray<int> setids;
  for (int j=0; j<datasetbrowser->size(); j++) {
    if (datasetbrowser->selected(j+1)) {
      setids.append(j);
    }
  }
  spec.nvolsets = setids.num();
  spec.setids = &(setids[0]);

  molid = app->molecule_load(molid, filename, filetype, &spec);
  spec.setids = NULL; // don't want FileSpec to delete the data!
  if (molid < 0) {
    fl_alert("Unable to load molecule.");
    return;
  }

  // Clear the filename input, set the filetype back to automatic, and 
  // change the molecule to the most recent one loaded, if any.  If we
  // didn't do this, when you went to select a new molecule, the old 
  // filetype would be retained, instead of being chosen from the filename
  // extension.  It seems less surprising this way.

  molchooser->value(0);
  for (int m=0; m<app->num_molecules(); m++) {
    if (molid == app->molecule_id(m)) {
      molchooser->value(m+1);
    }
  }
  filenameinput->value("");
  filetypechooser->value(0);
  update_checkboxes();
}
      

void FileChooserFltkMenu::make_window() {
  size(450,250);

  { 
    { Fl_Choice* o = molchooser = new Fl_Choice(109, 10, 320, 25, "Load files for: ");
      o->box(FL_THIN_UP_BOX);
      o->down_box(FL_BORDER_BOX);
      o->color(VMDMENU_CHOOSER_BG);
      o->selection_color(VMDMENU_CHOOSER_SEL);
    }
    filenameinput = new Fl_Input(85, 40, 270, 25, "Filename: ");
    filenameinput->callback(filenameinput_cb, this);
    filenameinput->when(FL_WHEN_ENTER_KEY_ALWAYS);
    filenameinput->selection_color(VMDMENU_VALUE_SEL);

    VMDFLTKTOOLTIP(filenameinput, "Enter a filename, or a 4-character PDB accession code")

    browsebutton = new Fl_Button(365, 40, 65, 25, "Browse...");
#if defined(VMDMENU_WINDOW)
    browsebutton->color(VMDMENU_WINDOW, FL_GRAY);
#endif
    browsebutton->callback(browse_cb, this);
    browsebutton->when(FL_WHEN_ENTER_KEY_ALWAYS | FL_WHEN_RELEASE);
    { Fl_Choice* o = filetypechooser = new Fl_Choice(20, 90, 300, 25, "Determine file type:");
      o->down_box(FL_BORDER_BOX);
      o->align(FL_ALIGN_TOP_LEFT);
      o->color(VMDMENU_CHOOSER_BG, VMDMENU_CHOOSER_SEL);
      o->callback(filetype_cb, this);

      VMDFLTKTOOLTIP(o, "Select the correct file type (if not already guessed correctly")
    }
    { Fl_Button* o = loadbutton = new Fl_Button(345, 90, 85, 25, "Load");
#if defined(VMDMENU_WINDOW)
      o->color(VMDMENU_WINDOW, FL_GRAY);
#endif
      VMDFLTKTOOLTIP(o, "Load the selected files")
      o->callback(load_cb, this);
    }
    { Fl_Group* o = timestepgroup = new Fl_Group(20, 145, 165, 95, "Frames: ");
      o->box(FL_ENGRAVED_FRAME);
      o->align(FL_ALIGN_TOP_LEFT);
      { Fl_Button* o = allatoncebutton = new Fl_Round_Button(30, 215, 150, 20, "Load all at once");
        o->down_box(FL_ROUND_DOWN_BOX);
        o->type(FL_RADIO_BUTTON);
      }
      { Fl_Button* o = loadinbackgroundbutton = new Fl_Round_Button(30, 195, 150, 20, "Load in background");
        o->down_box(FL_ROUND_DOWN_BOX);
        o->type(FL_RADIO_BUTTON);
      }
      { Fl_Input* o = firstinput = new Fl_Int_Input(25, 170, 45, 20, "First:");
        o->align(FL_ALIGN_TOP);
        o->selection_color(VMDMENU_VALUE_SEL);
      }
      { Fl_Input* o = lastinput = new Fl_Int_Input(80, 170, 45, 20, "Last:");
        o->align(FL_ALIGN_TOP);
        o->selection_color(VMDMENU_VALUE_SEL);
      }
      { Fl_Input* o = strideinput = new Fl_Int_Input(135, 170, 45, 20, "Stride:");
        o->align(FL_ALIGN_TOP);
        o->selection_color(VMDMENU_VALUE_SEL);
      }
      o->end();
      datasetbrowser = new Fl_Multi_Browser(195, 145, 235, 95, "Volumetric Datasets");
      datasetbrowser->align(5);
      datasetbrowser->color(VMDMENU_BROWSER_BG, VMDMENU_BROWSER_SEL);
    }
    Fl_Window::end();
  }
}


FileChooserFltkMenu::FileChooserFltkMenu(VMDApp *vmdapp)
: VMDFltkMenu("files", "Molecule File Browser", vmdapp) {

  make_window();

  command_wanted(Command::PLUGIN_UPDATE);
  command_wanted(Command::MOL_NEW);
  command_wanted(Command::MOL_DEL);
  command_wanted(Command::MOL_RENAME);

  update_molchooser();
  update_filetypes();
  update_checkboxes();
}


int FileChooserFltkMenu::selectmol(int molno) {
  update_molchooser(molno);
  return TRUE;
}


int FileChooserFltkMenu::act_on_command(int type, Command *cmd) {
  switch (type) {
    case Command::PLUGIN_UPDATE:
      update_filetypes();
      break;
    case Command::MOL_NEW:
    case Command::MOL_DEL:
    case Command::MOL_RENAME:
      update_molchooser();
      break;
    default:
      return 0;
  }
  return 1;
}
